package com.egg.mall.service.category.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.egg.common.core.constant.CoreConstant;
import com.egg.common.core.exception.CustomException;
import com.egg.common.mybatis.service.impl.BaseServiceImpl;
import com.egg.common.util.JsonUtil;
import com.egg.mall.entity.category.Category;
import com.egg.mall.mapper.category.CategoryMapper;
import com.egg.mall.model.request.CategoryPostOrPut;
import com.egg.mall.model.response.web.CategoryTree;
import com.egg.mall.service.category.ICategoryAttributeService;
import com.egg.mall.service.category.ICategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * @author east
 * @since 2022-08-08
 */
@Slf4j
@Service
public class CategoryServiceImpl extends BaseServiceImpl<CategoryMapper, Category> implements ICategoryService {


    @Autowired
    private ICategoryAttributeService attributeService;


    /**
     * 名称是否重复
     *
     * @param name 名称
     * @return boolean true 存在 / false 不存在
     */
    public boolean existName(String name) {
        return existName(name, Optional.empty());
    }

    /**
     * 名称是否重复
     *
     * @param name     名称
     * @param filterId 排除id
     * @return boolean true 存在 / false 不存在
     */
    public boolean existName(String name, Optional<? extends Serializable> filterId) {
        if (ObjectUtil.isEmpty(name)) {
            return false;
        }
        LambdaQueryWrapper<Category> qw = buildLambdaQw();
        qw.eq(Category::getName, name);
        filterId.ifPresent(o -> qw.notIn(Category::getId, o));
        return count(qw) > 0;
    }

    @Override
    public List<CategoryTree> tree() {
        LambdaQueryWrapper<Category> qw = buildLambdaQw();
        qw.orderByAsc(Category::getSort);
        List<Category> dbList = list(qw);
        if (ObjectUtil.isEmpty(dbList)) {
            return Collections.emptyList();
        }
        List<Category> topList = dbList.stream()
                .filter(o -> Objects.equals(o.getPid(), CoreConstant.DEFAULT_ID))
                .collect(Collectors.toList());
        Map<Long, List<Category>> childrenDeptGroup = dbList.stream()
                .filter(o -> !Objects.equals(o.getPid(), CoreConstant.DEFAULT_ID))
                .collect(Collectors.groupingBy(o -> o.getPid()));
        List<CategoryTree> tree = new ArrayList<>();
        topList.forEach(o -> {
            CategoryTree topTree = JsonUtil.objToObj(o, CategoryTree.class);
            topTree.setChildren(convertTree(o, childrenDeptGroup));
            tree.add(topTree);
        });
        return tree;
    }

    @Override
    public List<CategoryTree> tree(Optional<Serializable> pid) {
        LambdaQueryWrapper<Category> qw = buildLambdaQw();
        qw.orderByAsc(Category::getSort);
        pid.ifPresent(o -> qw.eq(Category::getPid, o));
        List<Category> dbList = list(qw);
        if (ObjectUtil.isEmpty(dbList)) {
            return Collections.emptyList();
        }
        List<Category> topList = dbList.stream()
                .filter(o -> Objects.equals(o.getPid(), CoreConstant.DEFAULT_ID))
                .collect(Collectors.toList());
        Map<Long, List<Category>> childrenDeptGroup = dbList.stream()
                .filter(o -> !Objects.equals(o.getPid(), CoreConstant.DEFAULT_ID))
                .collect(Collectors.groupingBy(o -> o.getPid()));
        List<CategoryTree> tree = new ArrayList<>();
        topList.forEach(o -> {
            CategoryTree topTree = JsonUtil.objToObj(o, CategoryTree.class);
            topTree.setChildren(convertTree(o, childrenDeptGroup));
            tree.add(topTree);
        });
        return tree;
    }

    /**
     * 转树
     *
     * @param p
     * @param childrenGroup
     * @return
     */
    private static List<CategoryTree> convertTree(Category p, Map<Long, List<Category>> childrenGroup) {
        List<Category> childrenDept = childrenGroup.getOrDefault(p.getId(), Collections.emptyList());
        if (ObjectUtil.isEmpty(childrenDept)) {
            return Collections.emptyList();
        }
        List<CategoryTree> tree = new ArrayList<>();
        childrenDept.forEach(o -> {
            CategoryTree temp = JsonUtil.objToObj(o, CategoryTree.class);
            temp.setChildren(convertTree(o, childrenGroup));
            tree.add(temp);
        });
        return tree;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Long add(CategoryPostOrPut param) {
        if (existName(param.getName())) {
            throw new CustomException("名称重复");
        }
        Category category = JsonUtil.objToObj(param, Category.class);
        Optional<Category> pCategory = baseFindById(param.getPid());
        if (pCategory.isPresent()) {
            category.setLevel(pCategory.get().getLevel() + 1);
        } else {
            category.setPid(CoreConstant.DEFAULT_ID);
            category.setLevel(1);
        }
        baseSave(category);
        return category.getId();
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public Long edit(Serializable id, CategoryPostOrPut param) {
        Category exist = baseGetById(id);
        if (existName(param.getName(), Optional.of(exist.getId()))) {
            throw new CustomException("名称重复");
        }
        Category info = JsonUtil.objToObj(param, Category.class);
        info.setId(exist.getId());
        baseSave(info);
        return info.getId();
    }

    @Override
    public List<Category> findAllSubById(Collection<? extends Serializable> id) {
        if (ObjectUtil.isEmpty(id)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<Category> qw = buildLambdaSortQw();
        qw.in(Category::getPid, id);
        List<Category> subList = list(qw);
        List<Category> nextSubList = findAllSubById(subList.stream().map(o -> o.getId()).collect(Collectors.toSet()));
        List<Category> allList = new ArrayList<>();
        allList.addAll(subList);
        allList.addAll(nextSubList);
        return allList;
    }

    @Override
    public List<Category> findAllSubAndCurrentById(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<Category> currentList = baseFindById(ids);
        List<Category> subList = findAllSubById(ids);
        List<Category> allList = new ArrayList<>();
        allList.addAll(currentList);
        allList.addAll(subList);
        return allList;
    }

    @Transactional(rollbackFor = Throwable.class)
    @Override
    public void delCategory(Collection<? extends Serializable> ids) {
        if (ObjectUtil.isEmpty(ids)) {
            return;
        }
        Set<Long> currentAndSubIds = findAllSubAndCurrentById(ids).stream().map(o -> o.getId()).collect(Collectors.toSet());
        this.baseDel(currentAndSubIds);
        attributeService.delByCategoryId(currentAndSubIds);
    }


}
